/**
 * \file	ComparaisonTemps.cpp
 * \brief	Main de test de temps d'execution d'une fonction avec et sans memoizer
 * \author	Thibaut ALGRIN
 * \author	Emmanuel CARRE
 * \version	0.1
 * \date	Décembre 2011
 */

#include <iostream>
#include <time.h>
#include <utility>
#include <time.h>
#include "Minuterie.h"
#include "Memoizer.h"
#include "ArbreAVLAdaptateur.h"
#include "MapAdaptateur.h"
#include "ListAdaptateur.h"
#include "TableAdaptateur.h"
#include "Table.h"

using namespace tp3;

typedef int (*fptr)(int);
typedef unsigned int (*fptr3)(unsigned int);
typedef int (*fptr2)(std::pair<int, int>);
typedef	bool (*fptr4)(unsigned int);

bool	collatz(unsigned int n);

class H1
{
public:
  unsigned long operator() (int x) const
  {
    return x % 100;
  }
  unsigned long operator() (unsigned int x) const
  {
    return x % 100;
  }
  unsigned long operator() (std::pair<int, int> x) const
  {
    return ((int)(x.first + x.second) % 100);
  }
};
/*
 * \brief fonction permettant d'afficher le temps d'execution
 * \param const double &
 * \param const double &
 * \param const double &
 * \param const double &
 * \param const double &
 * \param const std::string &
 * \fn void afficher(const double & t1, const double & t2, const double & t3, const double & t4, const double & t5, const std::string & fonc)
 * \return void
 */
void afficher(const double & t1, const double & t2, const double & t3, const double & t4, const double & t5, const std::string & fonc)
{
  std::cout << "------------------------" << std::endl;
  std::cout << "FONCTION:" << fonc << std::endl;
  std::cout << "Sans memoisation:         " << t1 << std::endl;
  std::cout << "Avec memoisation - AVL:   " << t2 << std::endl;
  std::cout << "Avec memoisation - Table: " << t3 << std::endl;
  std::cout << "Avec memoisation - List:  " << t4 << std::endl;
  std::cout << "Avec memoisation - Map:   " << t5 << std::endl;
}

/*
 * \brief fonction permettant d'incrementer une variable passée en paramettre
 * \param int
 * \return int
 * \fn int	incrementer(int n)
 */
int	incrementer(int n)
{
  return (n + 1);
}

/*
 * \brief fonction permettant de calculer la factoriel d'une variable passée en paramettre
 * \param int
 * \return int
 * \fn int	factoriel(int n)
 */
int	factoriel(int n)
{
  if (n == 0)
    return (0);
  return (n * factoriel(n - 1));
}

/*
 * \brief fonction permettant d'additionner deux nombre présent dans une pair passée en paramettre
 * \param std::pair<int, int>
 * \return int
 * \fn int addition(std::pair<int, int> nb)
 */
int addition(std::pair<int, int> nb)
{
  return (nb.first + nb.second);
}

/*
 * \brief fonction permettant de calculer la suite de Fibonacci
 * \param unsigned int n
 * \return unsigned int
 * \fn unsigned int	fibonacciRec(unsigned int n)
 */
unsigned int	fibonacciRec(unsigned int n)
{
  if (n < 2)
    return (n);
  return (fibonacciRec(n - 1) + fibonacciRec(n - 2));
}

/*
 * \brief fonction permettant de calculer la suite de Fibonacci en faisant appel à la class Memoizer
 * \param unsigned int n
 * \return unsigned int
 * \fn unsigned int	fibonacci(unsigned int n)
 */
unsigned int	fibonacci(unsigned int n)
{
  static Memoizer< unsigned int , unsigned int , MapAdaptateur< unsigned int, unsigned int > >	memo(fibonacciRec);
  return (memo(n));
}

/*
 * \brief fonction permettant de calculer la conjoncture de Collatz
 * \param unsigned int n
 * \return bool
 * \fn bool	collatzRec(unsigned int n)
 */
bool	collatzRec(unsigned int n)
{
  if (n < 2)
    return (true);
  if ((n % 2) == 0)
    return (collatz(n / 2));
  else
    return (collatz(3 * n) + 1);
}

/*
 * \brief fonction permettant de calculer la conjoncture de Collatz en faisant appel à la class Memoizer
 * \param unsigned int n
 * \return bool
 * \fn bool	collatzRec(unsigned int n)
 */
bool	collatz(unsigned int n)
{
  static	Memoizer<unsigned int, bool, MapAdaptateur<unsigned int, bool> >	memo(collatzRec);
  return (memo(n));
}

/*
 * \brief fonction permettant de tester la fonction Incrementer avec les différent type de conteneur
 * \fn void	testIncrementer(timer &t)
 * \return void
 * \param timer &t
 */
void	testIncrementer(timer &t)
{
  double	t1 = 0;
  double	t2 = 0;
  double	t3 = 0;
  double	t4 = 0;
  double	t5 = 0;
  fptr  ptr = &incrementer;

  srand(time(NULL));
  Memoizer< int, int, ArbreAVLAdaptateur<int, int> > 			memo(ptr);
  Memoizer< int, int, TableAdaptateur<int, int, H1> > 	        memo1(ptr);
  Memoizer< int, int, ListAdaptateur<int, int> > 			memo2(ptr);
  Memoizer< int, int, MapAdaptateur<int, int> > 			memo3(ptr);
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    incrementer(rand() % 5);
  t1 = t.elapsed();
  for(long x = 0; x < 10000000 ; x++)
    memo(rand() % 5);
  t2 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo1(rand() % 5);
  t3 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo2(rand() % 5);
  t4 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo3(rand() % 5);
  t5 = t.elapsed();
  afficher(t1, t2, t3, t4, t5, "incrementer");
}

/*
 * \brief fonction permettant de tester la fonction Addition avec les différent type de conteneur
 * \fn void	testAddition(timer &t)
 * \return void
 * \param timer &t
 */
void	testAddition(timer &t)
{
  double	t1 = 0;
  double	t2 = 0;
  double	t3 = 0;
  double	t4 = 0;
  double	t5 = 0;
  fptr2	ptr = &addition;

  srand(time(NULL));
  Memoizer< std::pair<int, int>, int, ArbreAVLAdaptateur<std::pair<int, int>, int> >							memo(ptr);
  Memoizer< std::pair<int, int>, int, TableAdaptateur<std::pair<int, int>, int, H1> > 	memo1(ptr);
  Memoizer< std::pair<int, int>, int, ListAdaptateur<std::pair<int, int>, int> >								memo2(ptr);
  Memoizer< std::pair<int, int>, int, MapAdaptateur<std::pair<int, int>, int> > 								memo3(ptr);
  std::pair<int, int> tmp(100, 100);
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    addition(tmp);
  t1 = t.elapsed();
  for(long x = 0; x < 10000000 ; x++)
    memo(tmp);
  t2 = t.elapsed();
  for(long x = 0; x < 10000000 ; x++)
    memo1(tmp);
  t3 = t.elapsed();
  for(long x = 0; x < 10000000 ; x++)
    memo2(tmp);
  t4 = t.elapsed();
  for(long x = 0; x < 10000000 ; x++)
    memo3(tmp);
  t5 = t.elapsed();
  afficher(t1, t2, t3, t4, t5, "addition");
}

/*
 * \brief fonction permettant de tester la fonction Factoriel avec les différent type de conteneur
 * \fn void	testFactoriel(timer &t)
 * \return void
 * \param timer &t
 */
void	testFactoriel(timer &t)
{
  double	t1 = 0;
  double	t2 = 0;
  double	t3 = 0;
  double	t4 = 0;
  double	t5 = 0;
  fptr	ptr = &factoriel;

  srand(time(NULL));
  Memoizer< int, int, ArbreAVLAdaptateur<int, int> >			memo(ptr);
  Memoizer< int, int, TableAdaptateur<int, int, H1> > 	                memo1(ptr);
  Memoizer< int, int, ListAdaptateur<int, int> > 			memo2(ptr);
  Memoizer< int, int, MapAdaptateur<int, int> > 			memo3(ptr);
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    factoriel(rand() % 5);
  t1 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo(rand() % 5);
  t2 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo1(rand() % 5);
  t3 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo2(rand() % 5);
  t4 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo3(rand() % 5);
  t5 = t.elapsed();
  afficher(t1, t2, t3, t4, t5, "factoriel");
}

/*
 * \brief fonction permettant de tester la fonction Fibonacci avec les différent type de conteneur
 * \fn void	testFibonacci(timer &t)
 * \return void
 * \param timer &t
 */
void	testFibonacci(timer &t)
{
  double	t1 = 0;
  double	t2 = 0;
  double	t3 = 0;
  double	t4 = 0;
  double	t5 = 0;
  fptr3	ptr = &fibonacci;

  srand(time(NULL));
  Memoizer< unsigned int, unsigned int, ArbreAVLAdaptateur<unsigned int, unsigned int> > 					memo(ptr);
  Memoizer< unsigned int, unsigned int, TableAdaptateur<unsigned int, unsigned int, H1> > 	memo1(ptr);
  Memoizer< unsigned int, unsigned int, ListAdaptateur<unsigned int, unsigned int> >						memo2(ptr);
  Memoizer< unsigned int, unsigned int, MapAdaptateur<unsigned int, unsigned int> > 						memo3(ptr);
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    fibonacci(rand() % 5);
  t1 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo(rand() % 5);
  t2 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo1(rand() % 5);
  t3 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo2(rand() % 5);
  t4 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo3(rand() % 5);
  t5 = t.elapsed();
  afficher(t1, t2, t3, t4, t5, "fibonacci");
}

/*
 * \brief fonction permettant de tester la fonction Collatz avec les différent type de conteneur
 * \fn void	testCollatz(timer &t)
 * \return void
 * \param timer &t
 */
void	testCollatz(timer &t)
{
  double	t1 = 0;
  double	t2 = 0;
  double	t3 = 0;
  double	t4 = 0;
  double	t5 = 0;
  fptr4	ptr = &collatz;

  srand(time(NULL));
  Memoizer< unsigned int, bool, ArbreAVLAdaptateur<unsigned int, bool> > memo(ptr);
  Memoizer< unsigned int, bool, TableAdaptateur<unsigned int, bool, H1> > 	memo1(ptr);
  Memoizer< unsigned int, bool, ListAdaptateur<unsigned int, bool> >				memo2(ptr);
  Memoizer< unsigned int, bool, MapAdaptateur<unsigned int, bool> > 				memo3(ptr);
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    collatz(rand() % 2);
  t1 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo(rand() % 2);
  t2 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo1(rand() % 2);
  t3 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo2(rand() % 2);
  t4 = t.elapsed();
  t.restart();
  for(long x = 0; x < 10000000 ; x++)
    memo3(rand() % 2);
  t5 = t.elapsed();
  afficher(t1, t2, t3, t4, t5, "collatz");
}

int	main(void)
{
  timer	t;

  testIncrementer(t);
  testAddition(t);
  testFactoriel(t);
  testFibonacci(t);
  testCollatz(t);
  return (0);
}

/*
------------------------
FONCTION:incrementer
Sans memoisation:         0.15
Avec memoisation - AVL:   0.65
Avec memoisation - Table: 0.53
Avec memoisation - List:  0.86
Avec memoisation - Map:   1.3
------------------------
FONCTION:addition
Sans memoisation:         0.04
Avec memoisation - AVL:   0.28
Avec memoisation - Table: 0.76
Avec memoisation - List:  1.1
Avec memoisation - Map:   2.03
------------------------
FONCTION:factoriel
Sans memoisation:         0.35
Avec memoisation - AVL:   0.48
Avec memoisation - Table: 0.57
Avec memoisation - List:  0.88
Avec memoisation - Map:   1.3
------------------------
FONCTION:fibonacci
Sans memoisation:         1.42
Avec memoisation - AVL:   0.47
Avec memoisation - Table: 0.52
Avec memoisation - List:  0.9
Avec memoisation - Map:   1.36
------------------------
FONCTION:collatz
Sans memoisation:         1.08
Avec memoisation - AVL:   0.38
Avec memoisation - Table: 0.5
Avec memoisation - List:  0.54
Avec memoisation - Map:   1.02
*/
